import json
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QTextEdit, QPushButton, QListWidget, QFrame, QSplitter,
                             QMessageBox, QFileDialog, QInputDialog, QLineEdit, QTreeView, QMenu)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QFont
from jsonpath_ng import parse
from datetime import datetime
import os
from PyQt5.QtCore import QAbstractItemModel, QModelIndex


class JSONTreeModel(QAbstractItemModel):
    def __init__(self, data, parent=None):
        super().__init__(parent)
        self.rootItem = self.create_item(data)

    def create_item(self, data, parent=None):
        if isinstance(data, dict):
            item = JSONItem(data, parent)
            for key, value in data.items():
                child = self.create_item(value, item)
                child.setData(key, 0)
                item.appendChild(child)
            return item
        elif isinstance(data, list):
            item = JSONItem(data, parent)
            for index, value in enumerate(data):
                child = self.create_item(value, item)
                child.setData(index, 0)
                item.appendChild(child)
            return item
        else:
            item = JSONItem(data, parent)
            if parent is None:
                # 根节点为简单类型时，设置默认键
                item.setData("root_value", 0)
            return item

    def rowCount(self, parent=QModelIndex()):
        if parent.isValid():
            return parent.internalPointer().childCount()
        return self.rootItem.childCount()

    def columnCount(self, parent=QModelIndex()):
        # 修改列数为 3，用于显示 Key、Value 和 Type
        return 3

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None
        item = index.internalPointer()
        if role == Qt.DisplayRole:
            if index.column() == 0:
                return item.key()
            elif index.column() == 1:
                value = item.value()
                # if isinstance(value, dict):
                #     return f"object({len(value)})"  # 将 dict 显示为 object
                # if isinstance(value, list):
                #     return f"{type(value).__name__}({len(value)})"
                return value
            elif index.column() == 2:
                value = item.value()
                if isinstance(value, dict):
                    return f"object({len(value)})"  # 将 dict 显示为 object
                if isinstance(value, list):
                    return f"{type(value).__name__}({len(value)})"
                # 直接从节点获取类型
                return item.type()
        return None

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            if section == 0:
                return "Key"
            elif section == 1:
                return "Value"
            elif section == 2:
                return "Type"
        return None

    def index(self, row, column, parent=QModelIndex()):
        if not self.hasIndex(row, column, parent):
            return QModelIndex()
        if not parent.isValid():
            parentItem = self.rootItem
        else:
            parentItem = parent.internalPointer()
        childItem = parentItem.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        return QModelIndex()

    def parent(self, index):
        if not index.isValid():
            return QModelIndex()
        childItem = index.internalPointer()
        parentItem = childItem.parent()
        if parentItem == self.rootItem or parentItem is None:
            return QModelIndex()
        return self.createIndex(parentItem.row(), 0, parentItem)


class JSONItem:
    def __init__(self, value, parent=None):
        self.parentItem = parent
        self.childItems = []
        self.itemValue = value
        self.itemKey = None
        # 新增属性，用于存储节点类型
        self.itemType = self._get_type(value)

    def _get_type(self, value):
        if isinstance(value, dict):
            return "object"
        elif isinstance(value, list):
            return "array"
        elif isinstance(value, str):
            return "string"
        elif isinstance(value, bool):
            return "boolean"
        elif isinstance(value, (int, float)):
            return "number"
        elif value is None:
            return "null"
        return "unknown"

    def appendChild(self, child):
        self.childItems.append(child)

    def child(self, row):
        return self.childItems[row]

    def childCount(self):
        return len(self.childItems)

    def row(self):
        if self.parentItem:
            return self.parentItem.childItems.index(self)
        return 0

    def parent(self):
        return self.parentItem

    def key(self):
        return self.itemKey

    def value(self):
        return self.itemValue

    def setData(self, key, column):
        if column == 0:
            self.itemKey = key

    # 新增方法，用于获取节点类型
    def type(self):
        return self.itemType


class JSONFormatterApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("JSON 格式化工具")
        self.setGeometry(100, 100, 1200, 800)

        # 设置整体样式
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f0f0f0;
            }
            QLabel {
                font-size: 14px;
                color: #333;
            }
            QPushButton {
                background-color: #FF8C00;
                color: white;
                border: none;
                padding: 6px 12px;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #D2B48C;
            }
            QTextEdit, QLineEdit, QListWidget, QTreeView {
                background-color: white;
                border: 1px solid #ccc;
                border-radius: 4px;
                padding: 5px;
            }
            QTextEdit:focus, QLineEdit:focus, QListWidget:focus, QTreeView:focus {
                border-color: #4CAF50;
                outline: none;
            }
            QFrame {
                background-color: white;
                border: 1px solid #ddd;
                border-radius: 4px;
            }
            QSplitter::handle {
                background-color: #ddd;
            }
        """)

        # 初始化历史记录
        self.history_file = os.path.expanduser("~/json_formatter_history.json")
        self.history = []
        self.load_history()

        # 初始化字体大小
        self.input_font_size = 10
        self.result_font_size = 10

        # 创建UI
        self.init_ui()

        # 自动格式化定时器
        self.auto_format_timer = QTimer()
        self.auto_format_timer.setSingleShot(True)
        self.auto_format_timer.timeout.connect(self.auto_format_if_needed)

    def init_ui(self):
        # 主窗口部件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        # 主布局
        main_layout = QHBoxLayout(main_widget)
        main_layout.setContentsMargins(5, 5, 5, 5)
        main_layout.setSpacing(5)

        # 分割器
        splitter = QSplitter(Qt.Horizontal)

        # 左侧历史记录区域
        self.history_frame = QFrame()
        self.history_frame.setFrameShape(QFrame.StyledPanel)
        history_layout = QVBoxLayout(self.history_frame)
        history_layout.setContentsMargins(5, 5, 5, 5)

        self.history_label = QLabel("历史记录")
        self.history_label.setAlignment(Qt.AlignCenter)
        history_layout.addWidget(self.history_label)

        self.history_list = QListWidget()
        self.history_list.setSelectionMode(QListWidget.SingleSelection)
        history_layout.addWidget(self.history_list)

        # 历史记录操作按钮
        self.history_btn_frame = QFrame()
        history_btn_layout = QHBoxLayout(self.history_btn_frame)
        history_btn_layout.setContentsMargins(0, 0, 0, 0)

        self.clear_history_btn = QPushButton("清空历史")
        self.clear_history_btn.clicked.connect(self.clear_history)
        history_btn_layout.addWidget(self.clear_history_btn)

        self.delete_history_btn = QPushButton("删除选中")
        self.delete_history_btn.clicked.connect(self.delete_selected_history)
        history_btn_layout.addWidget(self.delete_history_btn)

        self.export_history_btn = QPushButton("导出历史")
        self.export_history_btn.clicked.connect(self.export_history)
        history_btn_layout.addWidget(self.export_history_btn)

        self.import_history_btn = QPushButton("导入历史")
        self.import_history_btn.clicked.connect(self.import_history)
        history_btn_layout.addWidget(self.import_history_btn)

        history_layout.addWidget(self.history_btn_frame)

        # 右侧主内容区域
        self.content_frame = QFrame()
        self.content_frame.setFrameShape(QFrame.StyledPanel)
        content_layout = QVBoxLayout(self.content_frame)
        content_layout.setContentsMargins(5, 5, 5, 5)

        # 输入区域
        self.input_label = QLabel("输入 JSON:")
        content_layout.addWidget(self.input_label)

        self.input_text = QTextEdit()
        self.input_text.setFont(QFont("Consolas", self.input_font_size))
        content_layout.addWidget(self.input_text)

        # 输入框字体调整按钮
        input_font_frame = QFrame()
        input_font_layout = QHBoxLayout(input_font_frame)
        input_font_layout.setContentsMargins(0, 0, 0, 0)

        self.input_font_increase_btn = QPushButton("字体大小 +")
        self.input_font_increase_btn.clicked.connect(self.increase_input_font_size)
        input_font_layout.addWidget(self.input_font_increase_btn)

        self.input_font_decrease_btn = QPushButton("字体大小 -")
        self.input_font_decrease_btn.clicked.connect(self.decrease_input_font_size)
        input_font_layout.addWidget(self.input_font_decrease_btn)

        content_layout.addWidget(input_font_frame)

        # 操作按钮区域
        self.action_frame = QFrame()
        action_layout = QHBoxLayout(self.action_frame)
        action_layout.setContentsMargins(0, 0, 0, 0)

        self.format_btn = QPushButton("格式化")
        self.format_btn.clicked.connect(self.format_json)
        action_layout.addWidget(self.format_btn)

        self.compact_btn = QPushButton("压缩")
        self.compact_btn.clicked.connect(self.compact_json)
        action_layout.addWidget(self.compact_btn)

        self.save_history_btn = QPushButton("保存到历史")
        self.save_history_btn.clicked.connect(self.save_to_history)
        action_layout.addWidget(self.save_history_btn)

        self.validate_btn = QPushButton("验证 JSON")
        self.validate_btn.clicked.connect(self.validate_json)
        action_layout.addWidget(self.validate_btn)

        content_layout.addWidget(self.action_frame)

        # JSONPath 查询区域
        self.jsonpath_frame = QFrame()
        jsonpath_layout = QVBoxLayout(self.jsonpath_frame)
        jsonpath_layout.setContentsMargins(0, 0, 0, 0)

        self.jsonpath_input_frame = QFrame()
        jsonpath_input_layout = QHBoxLayout(self.jsonpath_input_frame)

        self.jsonpath_label = QLabel("JSONPath 查询:")
        jsonpath_input_layout.addWidget(self.jsonpath_label)

        self.jsonpath_input = QLineEdit()
        jsonpath_input_layout.addWidget(self.jsonpath_input)

        # 连接 returnPressed 信号到 query_jsonpath 方法，这行代码将 QLineEdit 的 returnPressed 信号（即用户按下回车键时发出的信号）连接到 query_jsonpath 方法
        self.jsonpath_input.returnPressed.connect(self.query_jsonpath)

        self.jsonpath_query_btn = QPushButton("查询")
        self.jsonpath_query_btn.clicked.connect(self.query_jsonpath)
        jsonpath_input_layout.addWidget(self.jsonpath_query_btn)

        jsonpath_layout.addWidget(self.jsonpath_input_frame)
        content_layout.addWidget(self.jsonpath_frame)

        # 结果显示区域分割器
        result_splitter = QSplitter(Qt.Horizontal)

        # 左侧结果显示区域
        self.result_label = QLabel("结果:")
        left_result_layout = QVBoxLayout()
        left_result_layout.addWidget(self.result_label)

        # 替换 QTextEdit 为 QTreeView
        self.result_tree = QTreeView()
        self.result_tree.setFont(QFont("Consolas", self.result_font_size))
        self.result_tree.setHeaderHidden(False)
        # 连接自定义的上下文菜单请求信号
        self.result_tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.result_tree.customContextMenuRequested.connect(self.show_result_tree_context_menu)
        left_result_layout.addWidget(self.result_tree)

        # 结果框字体调整按钮
        result_font_frame = QFrame()
        result_font_layout = QHBoxLayout(result_font_frame)
        result_font_layout.setContentsMargins(0, 0, 0, 0)

        self.result_font_increase_btn = QPushButton("字体大小 +")
        self.result_font_increase_btn.clicked.connect(self.increase_result_font_size)
        result_font_layout.addWidget(self.result_font_increase_btn)

        self.result_font_decrease_btn = QPushButton("字体大小 -")
        self.result_font_decrease_btn.clicked.connect(self.decrease_result_font_size)
        result_font_layout.addWidget(self.result_font_decrease_btn)

        left_result_layout.addWidget(result_font_frame)

        left_result_widget = QWidget()
        left_result_widget.setLayout(left_result_layout)
        result_splitter.addWidget(left_result_widget)

        # 右侧结果显示区域
        self.jsonpath_result_label = QLabel("JSONPath 查询结果:")
        right_result_layout = QVBoxLayout()
        right_result_layout.addWidget(self.jsonpath_result_label)

        self.jsonpath_result_text = QTextEdit()
        self.jsonpath_result_text.setReadOnly(True)
        self.jsonpath_result_text.setFont(QFont("Consolas", self.result_font_size))
        right_result_layout.addWidget(self.jsonpath_result_text)

        right_result_widget = QWidget()
        right_result_widget.setLayout(right_result_layout)
        result_splitter.addWidget(right_result_widget)

        content_layout.addWidget(result_splitter)

        # 添加到分割器
        splitter.addWidget(self.history_frame)
        splitter.addWidget(self.content_frame)
        splitter.setSizes([200, 1000])

        # 添加到主布局
        main_layout.addWidget(splitter)

        # 更新历史记录列表
        self.update_history_list()

        # 连接信号
        self.history_list.itemDoubleClicked.connect(self.on_history_select)
        self.input_text.textChanged.connect(self.on_input_changed)

    def show_result_tree_context_menu(self, pos):
        """显示结果树视图的上下文菜单"""
        index = self.result_tree.indexAt(pos)
        if index.isValid():
            menu = QMenu(self)
            copy_action = menu.addAction("复制")
            action = menu.exec_(self.result_tree.viewport().mapToGlobal(pos))
            if action == copy_action:
                self.copy_result_item(index)

    def copy_result_item(self, index):
        """复制结果树视图中选中项的键、值和类型"""
        item = index.internalPointer()
        key = item.key()
        value = item.value()
        item_type = item.type()

        # 处理键可能为空的情况
        if key is None:
            key = ""

        if isinstance(value, (dict, list)):
            try:
                value_text = json.dumps(value, ensure_ascii=False, indent=4)
            except Exception:
                value_text = str(value)
        else:
            value_text = str(value)

        # 组合键、值和类型信息
        text = f"Key: {key}\nValue: {value_text}\nType: {item_type}"
        clipboard = QApplication.clipboard()
        clipboard.setText(text)


    def increase_input_font_size(self):
        self.input_font_size += 1
        self.input_text.setFont(QFont("Consolas", self.input_font_size))

    def decrease_input_font_size(self):
        if self.input_font_size > 1:
            self.input_font_size -= 1
            self.input_text.setFont(QFont("Consolas", self.input_font_size))

    def increase_result_font_size(self):
        self.result_font_size += 1
        self.result_tree.setFont(QFont("Consolas", self.result_font_size))

    def decrease_result_font_size(self):
        if self.result_font_size > 1:
            self.result_font_size -= 1
            self.result_tree.setFont(QFont("Consolas", self.result_font_size))

    def on_input_changed(self):
        # 延迟自动格式化
        self.auto_format_timer.start(500)  # 500毫秒后执行

    def auto_format_if_needed(self):
        # 简单的自动格式化逻辑
        pass

    def load_history(self):
        try:
            if os.path.exists(self.history_file):
                with open(self.history_file, 'r', encoding='utf-8') as f:
                    self.history = json.load(f)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"加载历史记录失败: {str(e)}")
            self.history = []

    def save_history(self):
        try:
            with open(self.history_file, 'w', encoding='utf-8') as f:
                json.dump(self.history, f, ensure_ascii=False, indent=2)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存历史记录失败: {str(e)}")

    def update_history_list(self):
        self.history_list.clear()
        for item in self.history:
            self.history_list.addItem(item.get("timestamp", "") + " - " + item.get("title", "无标题"))

    def save_to_history(self):
        json_text = self.input_text.toPlainText().strip()
        if not json_text:
            QMessageBox.warning(self, "警告", "没有可保存的内容")
            return

        try:
            # 尝试解析JSON以验证
            json.loads(json_text)

            title, ok = QInputDialog.getText(self, "保存到历史", "输入标题:", text=f"JSON {len(self.history) + 1}")
            if not ok:
                return

            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

            new_item = {
                "title": title,
                "timestamp": timestamp,
                "content": json_text,
                "formatted": self.format_json_text(json_text) if self.is_json(json_text) else json_text
            }

            self.history.insert(0, new_item)
            self.save_history()
            self.update_history_list()
            QMessageBox.information(self, "成功", "已保存到历史记录")
        except json.JSONDecodeError as e:
            QMessageBox.critical(self, "错误", f"无效的JSON格式: {str(e)}")

    def on_history_select(self, item):
        index = self.history_list.row(item)
        if index < len(self.history):
            selected_item = self.history[index]
            self.input_text.setPlainText(selected_item["content"])

            # 尝试自动格式化
            if self.is_json(selected_item["content"]):
                self.format_json()

    def clear_history(self):
        if not self.history:
            return

        reply = QMessageBox.question(self, "确认", "确定要清空所有历史记录吗？",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.history = []
            self.save_history()
            self.update_history_list()

    def delete_selected_history(self):
        items = self.history_list.selectedItems()
        if not items:
            return

        reply = QMessageBox.question(self, "确认", "确定要删除选中的历史记录吗？",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            for item in items:
                index = self.history_list.row(item)
                if index < len(self.history):
                    del self.history[index]
            self.save_history()
            self.update_history_list()

    def export_history(self):
        if not self.history:
            QMessageBox.warning(self, "警告", "没有历史记录可导出")
            return

        file_path, _ = QFileDialog.getSaveFileName(
            self, "导出历史记录", "", "JSON 文件 (*.json);;所有文件 (*)"
        )

        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(self.history, f, ensure_ascii=False, indent=2)
                QMessageBox.information(self, "成功", "历史记录已导出")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"导出失败: {str(e)}")

    def import_history(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "导入历史记录", "", "JSON 文件 (*.json);;所有文件 (*)"
        )

        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    new_history = json.load(f)

                if isinstance(new_history, list):
                    self.history.extend(new_history)
                    self.save_history()
                    self.update_history_list()
                    QMessageBox.information(self, "成功", "历史记录已导入")
                else:
                    QMessageBox.critical(self, "错误", "导入的文件格式不正确")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"导入失败: {str(e)}")

    def format_json(self):
        json_text = self.input_text.toPlainText().strip()
        if not json_text:
            return

        try:
            obj = json.loads(json_text)
            formatted_text = json.dumps(obj, ensure_ascii=False, indent=4, sort_keys=True)
            # 直接更新输入框内容
            self.input_text.setPlainText(formatted_text)
            data = json.loads(formatted_text)
            model = JSONTreeModel(data)
            self.result_tree.setModel(model)
        except json.JSONDecodeError as e:
            QMessageBox.critical(self, "错误", f"无效的JSON格式: {str(e)}")

    def format_json_text(self, json_text):
        obj = json.loads(json_text)
        return json.dumps(obj, ensure_ascii=False, indent=4, sort_keys=True)

    def compact_json(self):
        json_text = self.input_text.toPlainText().strip()
        if not json_text:
            return

        try:
            obj = json.loads(json_text)
            compact_text = json.dumps(obj, ensure_ascii=False, separators=(',', ':'))
            # 直接更新输入框内容
            self.input_text.setPlainText(compact_text)
            data = json.loads(compact_text)
            model = JSONTreeModel(data)
            self.result_tree.setModel(model)
        except json.JSONDecodeError as e:
            QMessageBox.critical(self, "错误", f"无效的JSON格式: {str(e)}")


    def validate_json(self):
        json_text = self.input_text.toPlainText().strip()
        if not json_text:
            QMessageBox.warning(self, "警告", "没有输入内容")
            return

        try:
            json.loads(json_text)
            QMessageBox.information(self, "成功", "JSON 格式有效")
        except json.JSONDecodeError as e:
            QMessageBox.critical(self, "错误", f"无效的JSON格式: {str(e)}")

    def query_jsonpath(self):
        json_text = self.input_text.toPlainText().strip()
        if not json_text:
            QMessageBox.warning(self, "警告", "没有输入JSON内容")
            return

        jsonpath_expr = self.jsonpath_input.text().strip()
        if not jsonpath_expr:
            QMessageBox.warning(self, "警告", "没有输入JSONPath表达式")
            return

        try:
            data = json.loads(json_text)
            expr = parse(jsonpath_expr)
            results = [match.value for match in expr.find(data)]

            if results:
                result_data = results

                model = JSONTreeModel(result_data)
                self.result_tree.setModel(model)

                # 显示 JSONPath 查询结果到右侧文本框
                try:
                    json_result = json.dumps(result_data, ensure_ascii=False, indent=4)
                    self.jsonpath_result_text.setPlainText(json_result)
                except Exception:
                    self.jsonpath_result_text.setPlainText(str(result_data))

            else:
                self.result_tree.setModel(None)
                self.jsonpath_result_text.clear()
                QMessageBox.information(self, "提示", "未找到匹配结果")
        except json.JSONDecodeError as e:
            QMessageBox.critical(self, "错误", f"无效的JSON格式: {str(e)}")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"查询失败: {str(e)}")

    def is_json(self, json_text):
        try:
            json.loads(json_text)
            return True
        except json.JSONDecodeError:
            return False


if __name__ == "__main__":
    app = QApplication([])
    window = JSONFormatterApp()
    window.show()
    app.exec_()